/**
 ******************************************************************************
 * @file	interface/fpga-2016.c
 * @brief	interface/fpga-2016.c

 * Copyright (C) 2017 桂林聚联科技有限公司, Inc. All Rights Reserved.
 fpga-2016 存储器通信

采用原始 fpga-2016 SPI通信协议

bug
    fpga 自身返回序列有缺陷，普通的控制命令容易错乱，主控端需要多次重复读取，
    返回OTDR数据是否有错误无法得知，
 *
 * @section Platform
 * 	-# linux-2.6
 * @section Library
 * 	-# null
 *
 * @section Depend
 *	-# spi_mxs.ko
 *	-# spidev.ko
 *	-# libpthread.so
 *
 * - 2016-10-21,MenglongWu,MenglongWoo@aliyun.com
*/

#include <autoconfig.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include <sys/ioctl.h>

#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <stdio.h>

#include "proginfo.h"
#include "fpga-tr700.h"
#include "printl.h"

#include "pthread.h"
// #include "fpga-otdr-cfg.h"
#include "fpga_encode.h"


#define CONFIG_READ_1000_POINT (1)

#define SPI_TP_BEGIN           _IOR(SPI_IOC_MAGIC, 5, __u8)
#define SPI_TP_COMMIT          _IOR(SPI_IOC_MAGIC, 6, __u8)

#ifdef NDEBUG
#define this_dbg(fmt, ...)
#else
#define this_dbg(fmt, ...) PRINTL(OTDR_DBG_LEVEL, C_DEBUG fmt C_NORMAL, ##__VA_ARGS__)
#endif
#define this_dbg(fmt, ...) PRINTL(OTDR_DBG_LEVEL, C_DEBUG fmt C_NORMAL, ##__VA_ARGS__)


#if defined(CONFIG_USE_DEBUG_ENV)
#define ENV_PRINT_E(fmt, ...)                              \
    if (env_print_e) {                                     \
	_ctstprint(C_ERROR, C_NORMAL, fmt, ##__VA_ARGS__); \
    }
#define ENV_PRINT_W(fmt, ...)                             \
    if (env_print_w) {                                    \
	_ctstprint(C_WARN, C_NORMAL, fmt, ##__VA_ARGS__); \
    }
#else
#define ENV_PRINT_E(fmt, ...)
#define ENV_PRINT_w(fmt, ...)
#endif

#if 1 || defined(CONFIG_USE_PTHREAD_FPGA_OTDR)
#define mutex_init(x, y) pthread_mutex_init((x), (y))
#define mutex_destroy(x) pthread_mutex_destroy((x))
#define mutex_lock(x)                \
    ioctl(dev->fd, SPI_TP_BEGIN, 0); \
    pthread_mutex_lock((x));
#define mutex_unlock(x)        \
    pthread_mutex_unlock((x)); \
    ioctl(dev->fd, SPI_TP_COMMIT, 0);
#else
#define mutex_init(x, y)
#define mutex_destroy(x)
#define mutex_lock(x)   ioctl(dev->fd, SPI_TP_BEGIN, 0);
#define mutex_unlock(x) ioctl(dev->fd, SPI_TP_COMMIT, 0);
#endif

#define ACK_CMD_OFFSET   (9)
#define ACK_CODE_OFFSET  (10)
#define ACK_CMD(x)       (*((x) + ACK_CMD_OFFSET))
#define ACK_CODE(x)      (*((x) + ACK_CODE_OFFSET))

// #define LEN_OTDR_CACHE (32000 * 4 + 4000)
#define LEN_OTDR_CACHE   (64000 * 4 + 4000)
#define CHKSUM_OFFSET    (14)
#define CUM_COUNT_OFFSET (12)


static unsigned int __fpga_transfer(struct fpga_dev *dev, unsigned char cmd, char *tbuf, int tlen, char *rbuf, int rlen, int wait);
// 兼容以前的参数，默认发送命令后读取结果要等待100us，使用__fpga_transfer可自定义等待时间
#define _fpga_transfer(dev, cmd, tbuf, tlen, rbuf, rlen) \
    __fpga_transfer(dev, cmd, tbuf, tlen, rbuf, rlen, 100)

#define _1MS          (1000)

#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static unsigned int _fpga_alive(struct fpga_dev *dev);
static const char  *dev_desc = "fpga-2016";
// char *otdr_cache = NULL;
/*
《fpga-2016 TR600plus—CPU与FPGA通信命令协议》 2016-11-4 协议格式
最小帧长度 10 Byte
-------------------------------------------------------------------------------
glink (5)  |  len (1) |  unuse1 (1)  |  cmd (1)  |  data (n)  |  checksum (2)
-------------------------------------------------------------------------------

《TR600plus—CPU与FPGA通信命令协议.doc》 2016-11-24 协议格式
最小帧长度 12 Byte
-------------------------------------------------------------------------------
glink (5)  |  len (2)  |  unuse1 (1)  | addr (1) |  cmd (1)  |  data (n)  |  checksum (2)
-------------------------------------------------------------------------------

FPGA返回格式
其中 cmd 固定为 0xf0
data 通常为 1-8 byte
-------------------------------------------------------------------------------
glink (5)  |  len (2)  |  unuse1 (1)  | addr (1) |  cmd (1)  |  data(n)
-------------------------------------------------------------------------------

*/
#define MIN_TX_PACK_SIZE (12)  // 最小发送帧长度
static struct fpga_2016
{
    char head[5];
    /*
    FPGA 里忽略 glink 与 cmd 之间的 4 byte
    */

#if 1
    char len;
    char unuse0;
#else
    short len;
#endif
    char unuse1;
    char addr;
    char cmd;
} __attribute__((__packed__));


struct fpga_tr700_private
{
};

// #define MIN_RX_PACK_SIZE (12)		// 最小接收帧长度
#define MIN_RX_PACK_SIZE     (38)  // debug


// unsigned char txbuf[100];
// unsigned char rxbuf[4000];


/* OTDR 测试数据命令 */
#define OTDR_RETURN          (0xf0)
#define OTDR_RESET           (0xe0)

#define OTDR_PARAM           (0x10)
#define OTDR_START           (0x11)
#define OTDR_STOP            (0x12)
#define OTDR_IS_FINISH       (0x13)
#define OTDR_REDLASER        (0x14)
#define FPGA_ALIVE           OTDR_ALIVE
#define OTDR_COM_LASER       (0x15)
#define FPGA_VER             OTDR_VER
#define OTDR_ROTDR           (0x20)
#define OTDR_APD_TEMP        (0x21)
#define OTDR_POWER           (0x22)
#define OTDR_PULSE_ENCODE    (0x23)
// #define OTDR_RD_REG             (0x24)  // TF500 对寄存器芯片进行读操作

#define OTDR_ALARM_APPEAR    (0x25)  // TMS400 告警输出 数码管显示
#define OTDR_ALARM_DISAPPEAR (0x26)  // TMS400 告警取消

#define OTDR_WD_REG          (0x27)  // TF500 对寄存器芯片进行写操作 2018-8-27
#define OTDR_RD_REG          (0x28)  // TF500 对寄存器芯片进行读操作 2018-8-27

#define OCID_PARAM           (0x30)
#define OCID_LEVEL           (0x31)
#define OCID_START           (0x32)
#define OCID_STOP            OTDR_STOP
#define OCID_READ_DATA       (0x33)

/* 数据错误试着次数 */
#define RETRY                (3)


//*****************************************************************************
// 公共部分
//*****************************************************************************
#if defined(CONFIG_USE_DEBUG_ENV)
int env_otdr_dbg_level    = 0;
int env_print_otdr_data   = 0;
int env_dbg_tx            = 0;
int env_dbg_rx            = 0;
int env_dbg_bit_otdr_data = 0;

int env_print_e         = 0;
int env_print_w         = 0;
int env_lowpower_thread = 0;
int env_unuse_cksum     = 0;
int env_speed           = 0;
#endif
char env_dev_led_run[128] = "\0";

int watchdog = 0;
#define TIMEOUT_S (15)

#define WDG_CLEAR()   \
    {                 \
	watchdog = 0; \
    }
#define WDG_IS_TIMEOUT() (watchdog > TIMEOUT_S)
#define WDG_TICK()  \
    {               \
	watchdog++; \
    }

struct env_val
{
    char *env;
    int  *val;
};

struct env_val g_env_val[] = {
    { "OTDR_DBG_LEVEL", &env_otdr_dbg_level },
    { "PRINT_OTDR_DATA", &env_print_otdr_data },
    { "DBG_TX", &env_dbg_tx },
    { "DBG_RX", &env_dbg_rx },
    { "DBG_BIT_OTDR_DATA", &env_dbg_bit_otdr_data },
    { "DBG_PRINT_E", &env_print_e },
    { "DBG_PRINT_W", &env_print_w },
    { "LOWPOWER_PFGA", &env_lowpower_thread },
    { "UNUSE_CKSUM", &env_unuse_cksum },
    { "SPEED", &env_speed },
    {},
};
void fpga_update_env()
{
    char *env = NULL;
#if defined(CONFIG_USE_DEBUG_ENV)

    env = getenv("OTDR_DBG_LEVEL");
    if (env) {
	env_otdr_dbg_level = env;
    }
    env = getenv("PRINT_OTDR_DATA");
    if (env) {
	env_print_otdr_data = atoi(env);
    }
    env = getenv("DBG_TX");
    if (env) {
	env_dbg_tx = atoi(env);
    }
    env = getenv("DBG_RX");
    if (env) {
	env_dbg_rx = atoi(env);
    }
    env = getenv("DBG_BIT_OTDR_DATA");
    if (env) {
	env_dbg_bit_otdr_data = env;
    }
    env = getenv("DBG_PRINT_E");
    if (env) {
	env_print_e = atoi(env);
    }
    printf("env %s\r\n", env);
    env = getenv("DBG_PRINT_W");
    if (env) {
	env_print_w = atoi(env);
    }
#endif
    env = getenv("DEVICE_LED_RUN");
    if (env) {
	strcpy(env_dev_led_run, env);
	// printf("环境变量成功\r\n");
    }
    else {
	// printf("没有环境变量\r\n");
    }

    env = getenv("LOWPOWER_PFGA");
    if (env) {
	env_lowpower_thread = atoi(env);
    }

    env = getenv("UNUSE_CKSUM");
    if (env) {
	env_unuse_cksum = atoi(env);
	printf("env_unuse_cksum %d\r\n", env_unuse_cksum);
    }

    env = getenv("SPEED");
    if (env) {
	env_speed = atoi(env);
    }
    struct env_val *p = g_env_val;
    printf("当前环境变量\r\n");
    while (p->env) {
	printf("\t%s=%d\r\n", p->env, *p->val);
	p++;
    }
}

#include <sys/prctl.h>

void thread_detect_busy()
{
#define NOW_NULL           (0)
#define NOW_FPGA_POWER_OFF (1)
#define NOW_FPGA_POWER_ON  (2)
    int status = NOW_NULL;
    prctl(PR_SET_NAME, __FUNCTION__);
    while (1) {
	if (WDG_IS_TIMEOUT()) {
	    // printf("do power down...\r\n");
	    if (status != NOW_FPGA_POWER_OFF) {
		// printf("do power down f\r\n");
		do_fpga_power(0);
		status = NOW_FPGA_POWER_OFF;
	    }
	}
	else {
	    // printf("do power up...\r\n");
	    if (status != NOW_FPGA_POWER_ON) {
		// printf("do power up f\r\n");
		do_fpga_power(1);
		status = NOW_FPGA_POWER_ON;
	    }
	}

	// printf("wdg %d\r\n", watchdog);
	sleep(1);
	WDG_TICK();
    }
}

void low_power_task()
{
    if (env_lowpower_thread) {
	static pthread_t g_readtimeout;
	pthread_create(&g_readtimeout, NULL, thread_detect_busy, NULL);
    }
}

static void __attribute__((constructor)) _load_env()
{
    fpga_update_env();
    low_power_task();
}


int _fpga_send_data(
	struct fpga_dev *dev,
	char             cmd,
	void            *t_buf,
	int              tlen,
	void            *r_buf,
	int              rlen)
{
    char rbuf[MIN_RX_PACK_SIZE];
    __fpga_transfer(dev, cmd, t_buf, tlen, r_buf, rlen, _1MS * 50);
    char	 *pbuf = r_buf;
    unsigned char ack  = ACK_CODE(pbuf);
    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	return 0;
    case 0x55:
	ENV_PRINT_W("Error\r\n");
	return -1;
    default:
	ENV_PRINT_E("unknow [ack] %d\r\n", ack);
	return -1;
    }
    return -1;
}
int fpga_send_data(
	struct fpga_dev *dev,
	char             cmd,
	void            *t_buf,
	int              tlen,
	void            *r_buf,
	int              rlen)
{
    mutex_lock(&dev->mutex);
    int ret = _fpga_send_data(dev, cmd, t_buf, tlen, r_buf, rlen);
    mutex_unlock(&dev->mutex);
    return ret;
}

/* 本接口可支持的软件版本 */
struct _fpga_table
{
    unsigned long soft_ver;
    unsigned long hw_ver;
    char	 *desc;
};

/*
 * 1 点亮LED；0关闭LED，另外还有hook shell
 */
void do_otdr_test_led(int on_off)
{
    char  strcmd[256];
    FILE *fp;

    snprintf(strcmd, sizeof(strcmd), "/sys/class/gpio/%s/value", env_dev_led_run);
    fp = fopen(strcmd, "w");
    if (fp) {

	fprintf(fp, "%d", on_off);
	fclose(fp);
    }
    else {
	// printf("没有IO\r\n");
    }


    // 其他shell
    switch (on_off) {
    case 1:
	snprintf(strcmd, sizeof(strcmd), "/app/do_otdr_test_start.sh");
	break;
    case 0:
	snprintf(strcmd, sizeof(strcmd), "/app/do_otdr_test_finish.sh");
	break;
    }

    if ((access(strcmd, F_OK)) != -1) {
	// printf("文件存在\r\n");
	system(strcmd);
    }
    else {
	// printf("no文件存在\r\n");
    }
}

/*
 * 1 点亮LED；0关闭LED，另外还有hook shell
 */
void do_fpga_power(int on_off)
{
    char strcmd[256];
    // FILE *fp;

    // snprintf(strcmd, sizeof(strcmd), "/sys/class/gpio/%s/value", env_dev_led_run);
    // fp = fopen(strcmd, "w");
    // if (fp) {

    //         fprintf(fp, "%d", on_off);
    //         fclose(fp);
    // }
    // else {
    //     // printf("没有IO\r\n");
    // }


    // 其他shell
    switch (on_off) {
    case 1:
	snprintf(strcmd, sizeof(strcmd), "/app/do_otdr_test_start.sh");
	break;
    case 0:
	snprintf(strcmd, sizeof(strcmd), "/app/do_otdr_test_finish.sh");
	break;
    }

    if ((access(strcmd, F_OK)) != -1) {
	// printf("文件存在\r\n");
	system(strcmd);
    }
    else {
	// printf("no文件存在\r\n");
    }
}

// static const struct _fpga_table table[] = {
// };
void memcpy_char(char *dst, char *src, int len)
{
    for (int i = 0; i < len; i++) {
	*dst = *src;
	dst++;
	src++;
    }
}


int fpga_close(struct fpga_dev *dev)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    do_fpga_power(0);
    mutex_destroy(&dev->mutex);
    close(dev->fd);


    // 全局的可能会引起其他线程崩溃，不同应用之间不用担心，依靠写时拷贝
#if 0
	free(dev);
	dev = NULL;
	free(otdr_cache);
	otdr_cache = NULL;
	printf("otdr_cache %x\r\n", otdr_cache);
#else
    free(dev->otdr_cache);
    dev->otdr_cache = NULL;
    free(dev);
    dev = NULL;
#endif
    return 0;
}

int fpga_open(char *device, struct fpga_dev **dev, unsigned long ispeed)
{
    // todo spinlock
    unsigned char mode  = 0;
    unsigned char bits  = 8;
    unsigned long speed = 16 * 1000 * 1000;
    // unsigned short delay;
    int fd;

    if (env_speed > 1000000) {
	speed = env_speed;
    }
    else {
	if (ispeed) {
	    speed = ispeed;
	}
    }
    do_fpga_power(1);
    fd = open(device, O_RDWR);
    if (fd == 0) {
	return -1;
    }

    // mode = SPI_MODE_3;//ok
    mode = 0;  // ok
    // // mode = SPI_MODE_1;//error
    // mode = SPI_MODE_0;//ok
    ioctl(fd, SPI_IOC_WR_MODE, &mode);
    ioctl(fd, SPI_IOC_RD_MODE, &mode);
    ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);

    struct fpga_dev *pdev;
    pdev = (struct fpga_dev *)malloc(sizeof(struct fpga_dev));
    if (pdev == NULL) {
	goto _fail1;
    }
    bzero(pdev, sizeof(struct fpga_dev));

    // todo spinlock
    pdev->fd = fd;
    mutex_init(&pdev->mutex, NULL);
    if (-1 == fpga_alive(pdev)) {
	goto _fail2;
    }
    *dev               = pdev;
    (*dev)->otdr_cache = (char *)malloc(LEN_OTDR_CACHE);
    if ((*dev)->otdr_cache == NULL) {
	ENV_PRINT_E("malloc otdr_cache \n");
	exit(1);
    }


    return 0;
_fail1:
    close(fd);
    return -1;
_fail2:
    fpga_close(pdev);
    return -2;
}

void fpga_save_32bit(char *fname, unsigned char *data, unsigned long len)
{
    unsigned long *plong;
    unsigned long  loop = len >> 2;
    unsigned long  tmp;
    FILE          *fp;
    fp    = fopen(fname, "wb");
    plong = (unsigned long *)data;

    for (int i = 0; i < loop; i++) {
	tmp = (unsigned long)data[0] +
	      (unsigned long)(data[1] << 8) +
	      (unsigned long)(data[2] << 16) +
	      (unsigned long)(data[3] << 24);

	fprintf(fp, "%8.8ld\r\n", *plong);
	plong++;
    }
    fclose(fp);
    return;
}
//*****************************************************************************
// End 公共部分
//*****************************************************************************

//*****************************************************************************
// OTDR 系列接口
//*****************************************************************************

#define CONFIG_FPGA_SHIFT 1
/**
 * @brief	由于帧错位，在一串接受数据里找到帧头
 * @param	data 错位的帧
 * @param	limit 只在limit之前查找帧头，data 在 limit 后还有数据，但不足一帧
 * @retval	其他 找到帧
 * @retval	<0 找不到帧
 */

static int _find_frame_start(char *data, int limit)
{
#if defined(CONFIG_FPGA_SHIFT)
    char *pdata;
    for (int i = 0; i < limit; i++) {
	pdata = data + i;
	if ('G' == pdata[0] &&
	    'L' == pdata[1] &&
	    'I' == pdata[2] &&
	    'N' == pdata[3] &&
	    'K' == pdata[4]) {
	    this_dbg("%s() find frame head [offset %d byte]\r\n", __FUNCTION__, i);
	    return i;
	}
    }
    return -1;
#else
    return -1;
#endif
}
/**
 * @brief	寻找OTDR数据开始位置
 * @param	data
 * @param	limit 最大检测长度
 * @retval	null
 * @remarks
 * @see
 */
#if defined(CONFIG__find_otdr_data_start)
static int _find_otdr_data_start(char *data, int limit)
{
#if 1  // defined(CONFIG_FPGA_SHIFT)
    char *pdata;
    for (int i = 0; i < limit; i++) {
	pdata = data + i;
	if ('#' == pdata[0] &&
	    '#' == pdata[4] &&
	    '#' == pdata[8]) {
	    if (i != 0) {
		this_dbg("%s() find otdr data head [offset %d byte]\r\n", __FUNCTION__, i);
	    }
	    return i;
	}
    }
    return -1;
#else
    return 0;
#endif
}
#endif

static int _fpga_otdr_start(struct fpga_dev *dev)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    char rbuf[MIN_RX_PACK_SIZE];
    int  retry = 0;

_again:
    _fpga_transfer(dev, OTDR_START, &dev->ch, sizeof(dev->ch), rbuf, ARRAY_SIZE(rbuf));

    unsigned char ack;
    int           offset = 0;
_CheckFrame:;
    ack = ACK_CODE(rbuf + offset);
    retry++;
    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	return 0;
    case 0x55:
	ENV_PRINT_W("Error\r\n");
	return -1;
    default:
	offset = _find_frame_start(rbuf, ARRAY_SIZE(rbuf) / 2);
	if (retry < RETRY || -1 == offset) {
	    ENV_PRINT_E("unknow\r\n");
	    offset = 0;
	    break;
	}
	else {
	    goto _CheckFrame;
	}
	break;
    }

    if (retry < RETRY) {
	goto _again;
    }
    ENV_PRINT_E("retry time out\r\n");
    return -1;
}
int fpga_otdr_start(struct fpga_dev *dev)
{
    do_otdr_test_led(1);
    mutex_lock(&dev->mutex);
    int ret = _fpga_otdr_start(dev);
    mutex_unlock(&dev->mutex);
    return ret;
}
int fpga_otdr_stop(struct fpga_dev *dev)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    char rbuf[MIN_RX_PACK_SIZE];

    do_otdr_test_led(0);
    mutex_lock(&dev->mutex);
    _fpga_transfer(dev, OTDR_STOP, &dev->ch, sizeof(dev->ch), rbuf, ARRAY_SIZE(rbuf));
    mutex_unlock(&dev->mutex);

    unsigned char ack = ACK_CODE(rbuf);
    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	return 0;
    case 0x55:
	ENV_PRINT_W("Error\r\n");
	return -1;
    default:
	ENV_PRINT_E("unknow\r\n");
	return -1;
    }
    return -1;
}

// 小端
#if 1
//#define _STREAM_TO_32(data) (((data)[0] << 0) | ((data)[1] << 8) | ((data)[2] << 16) | ((data)[3] << 24))
//#define _STREAM_TO_16(data) (((data)[0] << 0) | ((data)[1] << 8))

// mips
#define _STREAM_TO_32(data) ((((data)[0] & 0xff) << 0) | (((data)[1] & 0xff) << 8) | (((data)[2] & 0xff) << 16) | (((data)[3] & 0xff) << 24))
#define _STREAM_TO_16(data) ((((data)[0] & 0xff) << 0) | (((data)[1] & 0xff) << 8))
#else
#endif
int fpga_otdr_isfinish(struct fpga_dev *dev, bool *val)
{
    assert(dev != NULL);
    // assert(dev->desc == dev_desc);

    // char rbuf[MIN_RX_PACK_SIZE + 6];
    char rbuf[MIN_RX_PACK_SIZE];

    mutex_lock(&dev->mutex);
    _fpga_transfer(dev, OTDR_IS_FINISH, &dev->ch, sizeof(dev->ch), rbuf, ARRAY_SIZE(rbuf));
    mutex_unlock(&dev->mutex);

    unsigned char ack;
    int           offset = 0;
    int           retry  = 0;

    *val = false;
_CheckFrame:;
    ack = ACK_CODE(rbuf + offset);
    retry++;
    switch (ack) {
    case 0xaa:
	dev->checksum  = _STREAM_TO_32(&rbuf[CHKSUM_OFFSET]);
	dev->cum_count = _STREAM_TO_16(&rbuf[CUM_COUNT_OFFSET]);
	// printf("checksum %lx\r\n", dev->checksum);
	*val = true;
	return 0;
    case 0x55:
	dev->checksum  = 0;
	dev->cum_count = 0;
	*val           = false;
	ENV_PRINT_E("Error\r\n");
	return 0;
    default:
	offset = _find_frame_start(rbuf, ARRAY_SIZE(rbuf) / 2);
	if (retry < RETRY || -1 == offset) {
#if defined(CONFIG_OTDR_PRINT_RX) && defined(CONFIG_ISFINISH_RX)
	    PrintfMemory((uint8_t *)rbuf, ARRAY_SIZE(rbuf));
	    ENV_PRINT_E("unknow\r\n");
#endif
	    offset = 0;
	    return -1;
	}
	else {
	    goto _CheckFrame;
	}
    }
    return -1;
}


/**
 * @brief	读取OTDR温度
 * @param	val 温度值被扩大100倍，50.23℃ -> 50230
 * @retval	null
 * @remarks
 * @see
 */

int fpga_otdr_temp(struct fpga_dev *dev, int *val)
{
    assert(dev != NULL);
    // assert(dev->desc == dev_desc);

    // char rbuf[MIN_RX_PACK_SIZE + 6];
    char          rbuf[MIN_RX_PACK_SIZE];
    unsigned char ack;
    int           offset = 0;
    int           retry  = 0;
    char          t;  // 温度带符号

    *val = false;
    for (int retry = 0; retry < RETRY; retry++) {
	mutex_lock(&dev->mutex);
	__fpga_transfer(dev, OTDR_APD_TEMP, NULL, 0, rbuf, ARRAY_SIZE(rbuf), 10000);
	mutex_unlock(&dev->mutex);

	ack = ACK_CODE(rbuf + offset);
	retry++;
	switch (ack) {
	case 0xaa:
#if 1 || MIPS
	    /*
	     12B：全0
	     13B：

	     当13B最高位为0时，直接读取，最高位1位1，取反-256得到温度，下面的例子表示-56度
	     12B  | 13B
	     0x00     | 0xc8
	    */
	    t = rbuf[13];
	    if (t < 0) {
		t = t - 256;
	    }
	    *val = t * 1000;
#else
	    *val = rbuf[12] * 1000 + rbuf[13] * 10;
#endif
	    // printf("*val %x %x\r\n", *val, rbuf[12]);
	    return 0;
	case 0x55:
	    ENV_PRINT_W("Error\r\n");
	    return 0;
	default:
	    ENV_PRINT_E("unknow\r\n");
	    break;
	}
    }
    return -1;
}

/*****************************************************************************
    设置 OTDR 测试参数 功能入口 fpga_otdr_param

 fpga_otdr_param
    _encode_dist_2_pulse/_encode_self_pulse
    _encode_dist_2_sample_freq/_encode_self_sample_freq
    _encode_laser
    _encode_adopt
    _encode_add_cnt
    _encode_power
    _encode_amp
    _encode_apd_vol
    _encode_slot
    _encode_ch
*****************************************************************************/
// static short _encode_laser(struct fpga_test_param *val);
// static short _encode_dist_2_pulse(struct fpga_test_param *val);
// static short _encode_self_pulse(struct fpga_test_param *val);
// static char  _encode_dist_2_sample_freq(struct fpga_test_param *val);
// static char  _encode_self_sample_freq(struct fpga_test_param *val);
// static char  _encode_adopt(struct fpga_test_param *val);
// static short  _encode_add_cnt(struct fpga_test_param *val);
// static char  _encode_amp(struct fpga_test_param *val);
// static char  _encode_apd_vol(struct fpga_test_param *val);
// static char  _encode_power(struct fpga_test_param *val);
// static char  _encode_slot(struct fpga_test_param *val);
// static char  _encode_ch(struct fpga_test_param *val);

/*
  struct _fpga_test_param_tx 内容依据《TR600plus—CPU与FPGA通信命令协议.doc》
  2016-11-24 协议格式而定

  实际发送 "数据段" 内容
 转译 struct fpga_test_param  用fpga_otdr_param() 屏蔽协议层、与协议无关
*/
// struct _fpga_test_param_tx {
// 	unsigned short laser;///< 激光器通道
// 	unsigned short pulse;
// 	unsigned short dist;
// 	// unsigned char freq;
// 	// unsigned char adopt_pt;
// 	unsigned short add_ms;
// 	char power;
// 	char amp;
// 	char apd_vol;
// 	char slot;
// 	char ch;	///< 光开关通道
// 	char unuse1, unuse2, unuse3;
// };

// struct _fpga_ocid_param_tx {
// 	char ch;
// 	short post;       // 位置（由FPGA自己决定起始、结束位置）
// 	// int post_from;  // 起始位置（由ARM处理器决定两位置）
// 	// int post_to;    // 结束位置（由ARM处理器决定两位置）
// } __attribute__ ((__packed__));

// static short _encode_laser(struct fpga_test_param *val)
// {
// 	// assert(1310 == val->laser_ch ||
// 	//        1550 == val->laser_ch ||
// 	//        1490 == val->laser_ch ||
// 	//        1625 == val->laser_ch ||
// 	//        1300 == val->laser_ch ||
// 	//        // 换一种描述方式
// 	//        1 == val->laser_ch ||
// 	//        2 == val->laser_ch ||
// 	//        3 == val->laser_ch ||
// 	//        4 == val->laser_ch ||
// 	//        5 == val->laser_ch);

// 	return val->laser_ch;
// }


// // 十进制转BCD码，并以16进制表示
// unsigned short __dec_2_bcd(unsigned short dec)
// {
// 	unsigned short bcd = 0, tmp;

// 	// tmp = dec / 1000;
// 	// dec = dec % 1000;
// 	// if (tmp) {
// 	// 	tmp = tmp << 12;
// 	// }

// 	tmp = dec / 100;
// 	dec = dec % 100;
// 	if (tmp) {
// 		tmp = tmp << 8;
// 	}
// 	bcd += tmp;

// 	tmp = dec / 10;
// 	dec = dec % 10;
// 	if (tmp) {
// 		tmp = tmp << 4;
// 	}
// 	bcd += tmp;

// 	bcd += dec;
// 	return bcd;
// }


/*
 * 量程（km） 0.3          1            2      5    ...    300          400
 * 编码      300         0x8000+1  0x8000+2  0x8000+5    0x8000+300  0x8000+400
 *          (十进制)     十六进制
 */
// static short _encode_pulse(struct fpga_test_param *val)
// {
// 	switch (val->pulse) {
// 	case 5:
// 		return val->pulse;
// 	case 10:
// 		return val->pulse;
// 	}

// 	return 0x8000 + val->pulse / 20;
// }

// static short _encode_dist(struct fpga_test_param *val)
// {

// 	if (val->dist == 300) {
// 		return val->dist;
// 	}

// 	return 0x8000 + val->dist / 1000;
// }


// static short _encode_self_pulse(struct fpga_test_param *val)
// {
// 	return val->pulse;
// }


// static short _encode_self_dist(struct fpga_test_param *val)
// {
// 	return val->dist;
// }


// **************************************************************************


// static char _encode_adopt(struct fpga_test_param *val)
// {
// 	switch(val->adopt_pt) {
// 	case 32000:
// 		return 8;
// 		break;
// 	case 4000:
// 		return 1;
// 		break;
// 	case 8000:
// 		return 2;
// 		break;
// 	case 16000:
// 		return 4;
// 		break;
// 	case 64000:
// 		return 16;
// 		break;
// 	case 128000:
// 		return 32;
// 		break;
// 	case 256000:
// 		return 64;
// 		break;
// 	}
// 	return 8;
// }

// static short _encode_add_cnt(struct fpga_test_param *val)
// {
// 	return val->add_cnt;
// }


//**************************************************************
// 有效参数等待 FPGA 解释
// 未必是下面的有效值
// #define UNUSE_BIT (0)

// #define AMP_LOW_ACTIVE_LV1   (CFG_AMP_LOW_ACTIVE_LV1)	// 光耦开关低电平有效
// #define AMP_LOW_ACTIVE_LV2   (CFG_AMP_LOW_ACTIVE_LV2)
// #define AMP_LOW_ACTIVE_LV3   (CFG_AMP_LOW_ACTIVE_LV3)
// #define AMP_LOW_ACTIVE_LV4   (CFG_AMP_LOW_ACTIVE_LV4)


// #define AMP_B0_TO_SW	(CFG_AMP_B0_TO_SW)	// bit0 映射到amp编码值 0x80上
// #define AMP_B1_TO_SW	(CFG_AMP_B1_TO_SW)
// #define AMP_B2_TO_SW	(CFG_AMP_B2_TO_SW)
// #define AMP_B3_TO_SW	(CFG_AMP_B3_TO_SW)
// #define AMP_B4_TO_SW	(CFG_AMP_B4_TO_SW)
// #define AMP_B5_TO_SW	(CFG_AMP_B5_TO_SW)
// #define AMP_B6_TO_SW	(CFG_AMP_B6_TO_SW)
// #define AMP_B7_TO_SW	(CFG_AMP_B7_TO_SW)

// #define AMP_B8_TO_SW	(CFG_AMP_B8_TO_SW)
// #define AMP_B9_TO_SW	(CFG_AMP_B9_TO_SW)
// #define AMP_B10_TO_SW	(CFG_AMP_B10_TO_SW)
// #define AMP_B11_TO_SW	(CFG_AMP_B11_TO_SW)
// #define AMP_B12_TO_SW	(CFG_AMP_B12_TO_SW)
// #define AMP_B13_TO_SW	(CFG_AMP_B13_TO_SW)
// #define AMP_B14_TO_SW	(CFG_AMP_B14_TO_SW)
// #define AMP_B15_TO_SW	(CFG_AMP_B15_TO_SW)

// #define AMP_B16_TO_SW	(CFG_AMP_B16_TO_SW)
// #define AMP_B17_TO_SW	(CFG_AMP_B17_TO_SW)
// #define AMP_B18_TO_SW	(CFG_AMP_B18_TO_SW)
// #define AMP_B19_TO_SW	(CFG_AMP_B19_TO_SW)
// #define AMP_B20_TO_SW	(CFG_AMP_B20_TO_SW)
// #define AMP_B21_TO_SW	(CFG_AMP_B21_TO_SW)
// #define AMP_B22_TO_SW	(CFG_AMP_B22_TO_SW)
// #define AMP_B23_TO_SW	(CFG_AMP_B23_TO_SW)

// #define AMP_B24_TO_SW	(CFG_AMP_B24_TO_SW)
// #define AMP_B25_TO_SW	(CFG_AMP_B25_TO_SW)
// #define AMP_B26_TO_SW	(CFG_AMP_B26_TO_SW)
// #define AMP_B27_TO_SW	(CFG_AMP_B27_TO_SW)
// #define AMP_B28_TO_SW	(CFG_AMP_B28_TO_SW)
// #define AMP_B29_TO_SW	(CFG_AMP_B29_TO_SW)
// #define AMP_B30_TO_SW	(CFG_AMP_B30_TO_SW)
// #define AMP_B31_TO_SW	(CFG_AMP_B31_TO_SW)

// // amp 各放大级别在 encode 里的有效掩码
// #define AMP_MASK_LV1 (CFG_AMP_MASK_LV1)
// #define AMP_MASK_LV2 (CFG_AMP_MASK_LV2)
// #define AMP_MASK_LV3 (CFG_AMP_MASK_LV3)
// #define AMP_MASK_LV4 (CFG_AMP_MASK_LV4)


// static char _encode_amp(struct fpga_test_param *val)
// {
// 	// 限制在5个可控档位以内
// 	char b0, b1, b2, b3, b4, b5, b6, b7;// 各比特位
// 	char level;
// 	char encode_lv1 = 0, encode_lv2 = 0, encode_lv3 = 0, encode_lv4 = 0;
// 	char encode = 0;

// #if AMP_MASK_LV1
// 	level = val->amp & 0xff;
// 	b0 = (level & 0x01) ? AMP_B0_TO_SW : 0;
// 	b1 = (level & 0x02) ? AMP_B1_TO_SW : 0;
// 	b2 = (level & 0x04) ? AMP_B2_TO_SW : 0;
// 	b3 = (level & 0x08) ? AMP_B3_TO_SW : 0;
// 	b4 = (level & 0x10) ? AMP_B4_TO_SW : 0;
// 	b5 = (level & 0x20) ? AMP_B5_TO_SW : 0;
// 	b6 = (level & 0x40) ? AMP_B6_TO_SW : 0;
// 	b7 = (level & 0x80) ? AMP_B7_TO_SW : 0;

// #if (AMP_LOW_ACTIVE_LV1 == 1)
// 	encode_lv1 =  ~(b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7);// 硬件高电平有效
// #else
// 	encode_lv1 =   (b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7);// 硬件低电平有效
// #endif
// 	encode_lv1 &= AMP_MASK_LV1;// 去掉无用 bit
// #endif // end AMP_MASK_LV1


// #if AMP_MASK_LV2
// 	level = (val->amp >> 8 ) & 0xff;
// 	b0 = (level & 0x01) ? AMP_B8_TO_SW : 0;
// 	b1 = (level & 0x02) ? AMP_B9_TO_SW : 0;
// 	b2 = (level & 0x04) ? AMP_B10_TO_SW : 0;
// 	b3 = (level & 0x08) ? AMP_B11_TO_SW : 0;
// 	b4 = (level & 0x10) ? AMP_B12_TO_SW : 0;
// 	b5 = (level & 0x20) ? AMP_B13_TO_SW : 0;
// 	b6 = (level & 0x40) ? AMP_B14_TO_SW : 0;
// 	b7 = (level & 0x80) ? AMP_B15_TO_SW : 0;
// #if (AMP_LOW_ACTIVE_LV2 == 1)
// 	encode_lv2 =  ~(b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7);
// #else
// 	encode_lv2 =   (b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7);
// #endif
// 	encode_lv2 &= AMP_MASK_LV2;
// #endif // end AMP_MASK_LV2


// #if AMP_MASK_LV3
// 	level = (val->amp >> 16 ) & 0xff;
// 	b0 = (level & 0x01) ? AMP_B16_TO_SW : 0;
// 	b1 = (level & 0x02) ? AMP_B17_TO_SW : 0;
// 	b2 = (level & 0x04) ? AMP_B18_TO_SW : 0;
// 	b3 = (level & 0x08) ? AMP_B19_TO_SW : 0;
// 	b4 = (level & 0x10) ? AMP_B20_TO_SW : 0;
// 	b5 = (level & 0x20) ? AMP_B21_TO_SW : 0;
// 	b6 = (level & 0x40) ? AMP_B22_TO_SW : 0;
// 	b7 = (level & 0x80) ? AMP_B23_TO_SW : 0;
// #if (AMP_LOW_ACTIVE_LV3 == 1)
// 	encode_lv3 =  ~(b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7);
// #else
// 	encode_lv3 =   (b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7);
// #endif
// 	encode_lv3 &= AMP_MASK_LV3;
// #endif // end AMP_MASK_LV3


// #if AMP_MASK_LV4
// 	level = (val->amp >> 24 ) & 0xff;
// 	b0 = (level & 0x01) ? AMP_B24_TO_SW : 0;
// 	b1 = (level & 0x02) ? AMP_B25_TO_SW : 0;
// 	b2 = (level & 0x04) ? AMP_B26_TO_SW : 0;
// 	b3 = (level & 0x08) ? AMP_B27_TO_SW : 0;
// 	b4 = (level & 0x10) ? AMP_B28_TO_SW : 0;
// 	b5 = (level & 0x20) ? AMP_B29_TO_SW : 0;
// 	b6 = (level & 0x40) ? AMP_B30_TO_SW : 0;
// 	b7 = (level & 0x80) ? AMP_B31_TO_SW : 0;

// #if (AMP_LOW_ACTIVE_LV4 == 1)
// 	encode_lv4 =  ~(b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7);
// #else
// 	encode_lv4 =   (b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7);
// #endif
// 	encode_lv4 &= AMP_MASK_LV4;
// #endif // end AMP_MASK_LV4

// 	encode = (encode_lv1 + encode_lv2 + encode_lv3 + encode_lv4) &
// 	         (AMP_MASK_LV1 + AMP_MASK_LV2 + AMP_MASK_LV3 + AMP_MASK_LV4);

// 	return encode;
// }


// //**************************************************************
// // 有效参数等待 FPGA 解释
// // 未必是下面的有效值

// #define APD_LOW_ACTIVE   (CFG_APD_LOW_ACTIVE)	// 光耦开关低电平有效
// #define APD_LOW_ACTIVE   (CFG_APD_LOW_ACTIVE)
// #define APD_LOW_ACTIVE   (CFG_APD_LOW_ACTIVE)
// #define APD_LOW_ACTIVE   (CFG_APD_LOW_ACTIVE)


// #define APD_B0_TO_SW	(CFG_APD_B0_TO_SW)	// bit0 映射到APD编码值 0x80上
// #define APD_B1_TO_SW	(CFG_APD_B1_TO_SW)
// #define APD_B2_TO_SW	(CFG_APD_B2_TO_SW)
// #define APD_B3_TO_SW	(CFG_APD_B3_TO_SW)
// #define APD_B4_TO_SW	(CFG_APD_B4_TO_SW)
// #define APD_B5_TO_SW	(CFG_APD_B5_TO_SW)
// #define APD_B6_TO_SW	(CFG_APD_B6_TO_SW)
// #define APD_B7_TO_SW	(CFG_APD_B7_TO_SW)

// #define APD_MASK (CFG_APD_MASK)

// static char _encode_apd_vol(struct fpga_test_param *val)
// {
// 	char b0, b1, b2, b3, b4, b5, b6, b7;// 各比特位
// 	char level;
// 	char encode = 0;

// #if APD_MASK
// 	level = val->apd_vol & 0xff;
// 	b0 = (level & 0x01) ? APD_B0_TO_SW : 0;
// 	b1 = (level & 0x02) ? APD_B1_TO_SW : 0;
// 	b2 = (level & 0x04) ? APD_B2_TO_SW : 0;
// 	b3 = (level & 0x08) ? APD_B3_TO_SW : 0;
// 	b4 = (level & 0x10) ? APD_B4_TO_SW : 0;
// 	b5 = (level & 0x20) ? APD_B5_TO_SW : 0;
// 	b6 = (level & 0x40) ? APD_B6_TO_SW : 0;
// 	b7 = (level & 0x80) ? APD_B7_TO_SW : 0;

// #if (APD_LOW_ACTIVE == 1)
// 	encode =  ~(b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7);// 硬件高电平有效
// #else
// 	encode =   (b0 ^ b1 ^ b2 ^ b3 ^ b4 ^ b5 ^ b6 ^ b7);// 硬件低电平有效
// #endif
// 	encode &= APD_MASK;// 去掉无用 bit
// #endif // end APD_LOW_ACTIVE

// 	return encode;
// }

// char _list_power[] = {
// 	0,
// 	OTDR_LASER_PW_LV1,
// 	OTDR_LASER_PW_LV2,
// 	OTDR_LASER_PW_LV3,
// 	OTDR_LASER_PW_LV4,
// };
// static char _encode_power(struct fpga_test_param *val)
// {
// 	assert(val->power < ARRAY_SIZE(_list_power));
// 	assert(0 <= val->power);

// 	return _list_power[(uint32_t)val->power];

// }
// static char _encode_slot(struct fpga_test_param *val)
// {
// 	// PRINTD("do nothing");
// #if defined(CONFIG_EN_CAP)
// 	return val->cap;
// #else
// 	return 0;
// #endif
// }
// static char _encode_ch(struct fpga_test_param *val)
// {
// 	// PRINTD("do nothing");
// 	return val->panel_ch;
// }

// static unsigned short _encode_add_ms(struct fpga_test_param *val)
// {
// 	return val->add_ms;
// }

// #define FREQ_OFFSET (12)
// #define ADOPT_PT_OFFSET (14)

// inline  unsigned long _decode_freq(char *rbuf)
// {
// 	unsigned short section;
// 	memcpy(&section, rbuf + FREQ_OFFSET, sizeof(unsigned short));
// 	switch(section) {
// 	case 12:
// 		return 12500000;
// 	case 6:
// 		return 6250000;
// 	default:
// 		break;
// 	}
// 	return section * 1000000;
// }
// inline unsigned short _decode_adopt_pt(char *rbuf)
// {
// 	unsigned short section;
// 	memcpy(&section, rbuf + ADOPT_PT_OFFSET, sizeof(unsigned short));
// 	return 	section * 1000;
// }

static int _fpga_otdr_param(struct fpga_dev *dev, struct fpga_test_param *val)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    struct _fpga_test_param_tx tbuf;

    /*
      当 sample_freq 未指定时使用默认采样频率，并计算脉冲宽度编码，
      否则使用自定义采样频率
    */
    if (val->en_hw_pulse == false) {
	tbuf.pulse = _encode_pulse(val);
    }
    else {
	tbuf.pulse = _encode_self_pulse(val);
    }
    if (val->en_hw_dist == false) {
	tbuf.dist = _encode_dist(val);
    }
    else {
	tbuf.dist = _encode_self_dist(val);
    }


    tbuf.laser = _encode_laser(val);
    tbuf.power = _encode_power(val);
    // printf("val->ms = %d\n", val->add_ms);
    tbuf.add_ms = _encode_add_ms(val);
    if (val->en_hw_amp) {
	tbuf.amp = val->hw_amp;
    }
    else {
	tbuf.amp = _encode_amp(val);
    }

    if (val->en_hw_apd_vol) {
	tbuf.apd_vol = val->hw_apd_vol;
    }
    else {
	tbuf.apd_vol = _encode_apd_vol(val);
    }
    tbuf.slot   = _encode_slot(val);
    tbuf.ch     = _encode_ch(val);
    tbuf.unuse1 = 0;
    tbuf.unuse2 = 0;
    tbuf.unuse3 = 0;


    this_dbg("Encode:\r\n");
    this_dbg("    laser             %4.4x\n", tbuf.laser);
    this_dbg("    pulse             %4.4x\n", tbuf.pulse);
    this_dbg("    dist              %4.4x\n", tbuf.dist);
    this_dbg("    add_ms            %4.4x\n", tbuf.add_ms);
    this_dbg("    power             %2.2x\n", tbuf.power);
    this_dbg("    amp               %2.2x\n", tbuf.amp);
    this_dbg("    apd_vol           %2.2x\n", tbuf.apd_vol);
    this_dbg("    slot              %2.2x\n", tbuf.slot);
    this_dbg("    ch                %2.2x\n", tbuf.ch);
    this_dbg("    unuse1            %2.2x\n", tbuf.unuse1);
    this_dbg("    unuse2            %2.2x\n", tbuf.unuse2);
    this_dbg("    unuse3            %2.2x\n", tbuf.unuse3);
    dev->ch = tbuf.ch;

    char rbuf[MIN_RX_PACK_SIZE];
    // ENV_PRINT_W("TODO MIN_RX_PACK_SIZE 不是TR600 返回长度");
    unsigned char ack;
    unsigned int  err_times = 0;
    // for (int retry = 0; retry < RETRY; retry++) {
    // 每个循环耗时5.4ms，最多等待1s
    for (int retry = 0; retry < 100; retry++) {
	_fpga_transfer(dev, OTDR_PARAM, (char *)&tbuf, sizeof(struct _fpga_test_param_tx), rbuf, ARRAY_SIZE(rbuf));
	ack = ACK_CODE(rbuf);
	switch (ack) {
	case 0xaa:
	    this_dbg("%s() OK\r\n", __FUNCTION__);
	    // usleep(10000);// FPGA控制光耦
	    goto _AnalyseOther;
	case 0x55:
	    // ENV_PRINT_W("Error\r\n");
	    err_times++;
	    break;
	default:
	    ENV_PRINT_E("unknow\r\n");
	    break;
	}
	usleep(10000);
    }
    if (err_times) {
	ENV_PRINT_W("Error times %d\r\n", err_times);
    }
    ENV_PRINT_E("retry time out\r\n");
    return -1;
_AnalyseOther:;
    if (err_times) {
	ENV_PRINT_W("Error times %d\r\n", err_times);
    }
    /* todo TR700需要返回
     sample_freq 采样频率 MHz
     adopt_pt  采样点数
     */

    // this_dbg("buf 12 %x \n", *(rbuf + 12));
    // *(rbuf + 11)  = 0x11;
    // *(rbuf + 12)  = 0x12;
    // *(rbuf + 13)  = 0x13;
    // *(rbuf + 14)  = 0x14;
    // *(rbuf + 15)  = 0x15;


    // PrintfMemory(rbuf, MIN_RX_PACK_SIZE);

    dev->sample_freq = _decode_freq(rbuf);
    dev->adopt_pt    = _decode_adopt_pt(rbuf);
    this_dbg("Decode:\r\n");
    this_dbg("    sample_freq       %d \n", (int)dev->sample_freq);
    this_dbg("    adopt_pt          %d \n", (int)dev->adopt_pt);
    return 0;
}
int fpga_otdr_param(struct fpga_dev *dev, struct fpga_test_param *val)
{
    mutex_lock(&dev->mutex);
    int ret = _fpga_otdr_param(dev, val);
    mutex_unlock(&dev->mutex);


    return ret;
}
int fpga_ocvl_param(struct fpga_dev *dev, struct fpga_test_param *val)
{
    mutex_lock(&dev->mutex);
    // _clear_fpga_cache(dev->fd);
    int ret = -1;  //_fpga_otdr_param(dev, val, OCVL_PARAM);
    // int ret = 0;
    mutex_unlock(&dev->mutex);
    return ret;
}
/*****************************************************************************
    End 设置 OTDR 测试参数 功能入口 fpga_otdr_param
****************************************************************************/

// 请求和开始测试一起
int fpga_request_test(struct fpga_dev *dev, struct fpga_test_param *val)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    mutex_lock(&dev->mutex);
    if (0 == _fpga_otdr_param(dev, val) &&
        0 == _fpga_otdr_start(dev)
        // TODO 等待数据准备完好，并带超市机制
    ) {
	mutex_unlock(&dev->mutex);
	return 0;
    }
    else {
	mutex_unlock(&dev->mutex);
	return -1;
    }
}

int fpga_red_laser(struct fpga_dev *dev, int val)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);
    assert(val == OTDR_RL_OFF || val == OTDR_RL_ON || val == OTDR_RL_1HZ || val == OTDR_RL_10HZ);

    char tbuf[2];
    // char rbuf[MIN_RX_PACK_SIZE];
    char rbuf[12];

    tbuf[0] = val;
    tbuf[1] = 0;
    mutex_unlock(&dev->mutex);
    _fpga_transfer(dev, OTDR_REDLASER, tbuf, ARRAY_SIZE(tbuf), rbuf, ARRAY_SIZE(rbuf));
    mutex_unlock(&dev->mutex);

    unsigned char ack = ACK_CODE(rbuf);

    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	return 0;
    case 0x55:
	ENV_PRINT_W("Error\r\n");
	return -1;
    default:
	ENV_PRINT_E("unknow\r\n");
	return -1;
    }
}


/**
 * @brief	FPGA电源
 * @param	val 1 打开，0关闭
 *
 * @bug
 * 			以后这个命令可能演化成FPGA电源控制，
 *			使用这个命令关闭电源后，目前状态下FPGA接收的命令全是正确，
 *			导致通信光检测一直有通信光
 */
int fpga_power(struct fpga_dev *dev, int val)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    char tbuf[2];
    char rbuf[MIN_RX_PACK_SIZE];

    tbuf[0] = val;
    tbuf[1] = 0;
    mutex_unlock(&dev->mutex);
    _fpga_transfer(dev, OTDR_POWER,
                   // tbuf, ARRAY_SIZE(tbuf),
                   NULL,
                   0,
                   rbuf,
                   ARRAY_SIZE(rbuf));
    mutex_unlock(&dev->mutex);

    unsigned char ack = ACK_CODE(rbuf);

    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	return 0;
    case 0x55:
	ENV_PRINT_W("Error\r\n");
	return -1;
    default:
	ENV_PRINT_E("unknow\r\n");
	return -1;
    }
}
#if defined(CONFIG_fpga_pulse)
int fpga_pulse(struct fpga_dev *dev, char (*val)[OTDR_PULSE_ARRAY])
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    char tbuf[OTDR_PULSE_ARRAY];
    char rbuf[MIN_RX_PACK_SIZE];

    memcpy(tbuf, val, OTDR_PULSE_ARRAY);
    mutex_unlock(&dev->mutex);
    _fpga_transfer(dev, OTDR_PULSE_ENCODE, tbuf, ARRAY_SIZE(tbuf), rbuf, ARRAY_SIZE(rbuf));
    mutex_unlock(&dev->mutex);

    unsigned char ack = ACK_CODE(rbuf);

    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	return 0;
    case 0x55:
	ENV_PRINT_W("Error\r\n");
	return -1;
    default:
	ENV_PRINT_E("unknow\r\n");
	return -1;
    }
}
#endif
/**
 * @brief	计算发送数据的校验码
 * @param[in]	data 待校验内容
 * @param[in]	len 待校验长度 byte 为单位
 * @retval	校验值
 */

unsigned short _tx_checksum(uint8_t *data, int len)
{
    unsigned short sum = 0;
    for (int i = 0; i < len; i++) {
	sum += data[i];
    }
    return sum;
}

unsigned char _remove_left_zero(unsigned char mask)
{
    if (0 == mask) {
	return 0;
    }
    while (0 == (mask & 0x01)) {
	mask = mask >> 1;
    }
    return mask;
}
/**
 * @brief	探测FPGA是否挂接到总线上
 * @param[in]	dev 设备描述符
 * @retval	0 找到设备
 * @retval	-1 没找到设备
 */
// static unsigned int _fpga_alive(struct fpga_dev *dev)
unsigned int _fpga_alive(struct fpga_dev *dev)
{
    assert(dev != NULL);
    // dev->desc = (char *)dev_desc;
    // return 0;

    // char rbuf[MIN_RX_PACK_SIZE];
    char rbuf[13];
    int  retry = 0;

_again:
    _fpga_transfer(dev, FPGA_ALIVE, NULL, 0, rbuf, ARRAY_SIZE(rbuf));
    unsigned char ack;
    int           offset = 0;

#ifdef CONFIG_IGNORE_FPGA_ALIVE
    this_dbg("%s() OK CONFIG_IGNORE_FPGA_ALIVE\r\n", __FUNCTION__);
    dev->desc     = (char *)dev_desc;
    dev->mask_amp = _remove_left_zero(CFG_AMP_MASK_LV1) |
                    (_remove_left_zero(CFG_AMP_MASK_LV2) << 8) |
                    (_remove_left_zero(CFG_AMP_MASK_LV3) << 16) |
                    (_remove_left_zero(CFG_AMP_MASK_LV4) << 24);
    dev->mask_apd = CFG_APD_MASK;
    return 0;
#endif
    dev->desc = (char *)dev_desc;
_CheckFrame:;
    ack = ACK_CODE(rbuf + offset);
    retry++;
    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	dev->mask_amp = _remove_left_zero(CFG_AMP_MASK_LV1) |
	                (_remove_left_zero(CFG_AMP_MASK_LV2) << 8) |
	                (_remove_left_zero(CFG_AMP_MASK_LV3) << 16) |
	                (_remove_left_zero(CFG_AMP_MASK_LV4) << 24);
	dev->mask_apd = CFG_APD_MASK;
	return 0;
    case 0x55:
	ENV_PRINT_W("Error\r\n");
	break;
    default:
	offset = _find_frame_start(rbuf, ARRAY_SIZE(rbuf) / 2);
	if (retry < RETRY || -1 == offset) {
	    // ENV_PRINT_E("unknow\r\n");
	    offset = 0;
	    break;
	}
	else {
	    goto _CheckFrame;
	}
    }
    if (retry < RETRY) {
	goto _again;
    }
    ENV_PRINT_E("retry time out\r\n");


    return -1;
}
unsigned int fpga_alive(struct fpga_dev *dev)
{
    mutex_lock(&dev->mutex);
    int ret = _fpga_alive(dev);
    mutex_unlock(&dev->mutex);
    return ret;
}
/**
 * @remarks	----    fpga-2016 不实现    ----
 */
int fpga_lcd_bl(struct fpga_dev *dev, int val)
{
    return 0;
}
/**
 * @remarks	----    fpga-2016 不实现    ----
 */
int fpga_charge_status(struct fpga_dev *dev, char *val)
{
    return 0;
}
/**
 * @remarks	----    fpga-2016 不实现    ----
 */
int fpga_low_power(struct fpga_dev *dev, int val)
{
    return 0;
}


int fpga_exist_com_laser(struct fpga_dev *dev, bool *val)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    char rbuf[MIN_RX_PACK_SIZE];

    mutex_lock(&dev->mutex);
    _fpga_transfer(dev, OTDR_COM_LASER, NULL, 0, rbuf, ARRAY_SIZE(rbuf));
    mutex_unlock(&dev->mutex);

    unsigned char ack = ACK_CODE(rbuf);

    switch (ack) {
    case 0xaa:
	this_dbg("%s() exist\r\n", __FUNCTION__);
	*val = true;
	return 0;
    case 0x55:
	this_dbg("%s() free\r\n", __FUNCTION__);
	*val = false;
	return 0;
    default:
	ENV_PRINT_E("unknow\r\n");
	*val = false;
	return -1;
    }
}

/* 接收到的版本信息转换成自然描述 */
#define HW_OFFSET   (11)
/* 文档里要求12byte，2年后赵奎反悔，说用从来没用过的保留位（第8byte），
说12byte内容他不会控制，每次返回都不一样，第8byte他可以控制 */
// #define SOFT_OFFSET (12)
#define SOFT_OFFSET (7)

#define HW_VER(x)   (*(x + HW_OFFSET))
#define SOFT_VER(x) (*(x + SOFT_OFFSET))
#define CH_VER(x)   (*(x + 11))


int fpga_version(struct fpga_dev *dev, char *soft_ver, char *hw_ver)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    char rbuf[MIN_RX_PACK_SIZE];

    mutex_lock(&dev->mutex);
    _fpga_transfer(dev, FPGA_VER, NULL, 0, rbuf, ARRAY_SIZE(rbuf));
    mutex_unlock(&dev->mutex);

    unsigned char ack = ACK_CODE(rbuf);
    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	ENV_PRINT_W("未验证");
	// todu 分离版本号
	*soft_ver = SOFT_VER(rbuf);
	*hw_ver   = HW_VER(rbuf);

	int ver4;
	// printf("拨码开关 %x\n", rbuf[11]);
	// printf("通道数   %x\n", rbuf[12]);
	// printf("保留     %x\n", rbuf[13]);
	memcpy(&ver4, rbuf + 14, 4);
	// printf("版本     %08x\n", ver4);
	return 0;
    case 0x55:
	ENV_PRINT_W("Error\r\n");
	return -1;
    default:
	ENV_PRINT_E("unknow\r\n");
	return -1;
    }
}

int fpga_info(struct fpga_dev *dev, struct fpga_info *out)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    char rbuf[MIN_RX_PACK_SIZE];


    mutex_lock(&dev->mutex);
    _fpga_transfer(dev, OTDR_VER, NULL, 0, rbuf, ARRAY_SIZE(rbuf));
    mutex_unlock(&dev->mutex);


    unsigned char ack = ACK_CODE(rbuf);
    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	int ver;
	out->hw_ver = rbuf[11];
	out->ch     = rbuf[12];
	memcpy(&ver, rbuf + 14, 4);
	out->soft_ver = ver;
	return 0;
    case 0x55:
	ENV_PRINT_W("Error\r\n");
	return -1;
    default:
	ENV_PRINT_E("unknow\r\n");
	return -1;
    }
}

void PrintfMemory(uint8_t *buf, uint32_t len)
{
    for (uint32_t i = 0; i < len; i++) {
	if (i & 0xf) {
	    printf(" ");
	}
	else {
	    printf("\r\n");
	}
	printf("%2.2x", (unsigned char)*(buf + i));
    }
    printf("\r\n");
}


#if CONFIG_OTDR_PRINT_TX
void _show_cmd_name(unsigned char cmd)
{
    static char *all[] = {
	[0x10] = "OTDR_PARAM",
	[0x11] = "OTDR_START",
	[0x12] = "OTDR_STOP",
	[0x13] = "OTDR_IS_FINISH",
	[0x14] = "OTDR_REDLASER",
	[0xa0] = "OTDR_ALIVE",
	[0x15] = "OTDR_COM_LASER",
	[0xa1] = "OTDR_VER",
	[0x20] = "OTDR_ROTDR",
	[0x21] = "OTDR_APD_TEMP",
	// [0x22]="OTDR_POWER",
	// [0x23]="OTDR_PULSE_ENCODE",
	[0x22] = "CMD_GPIO",
	[0x23] = "CMD_MDIO",
	[0x16] = "OCVL_PARAM",

	[0x25]  = "OTDR_ALARM_APPEAR",
	[0x26]  = "OTDR_ALARM_DISAPPEAR",
	[0x27]  = "OTDR_WD_REG",
	[0x28]  = "OTDR_RD_REG",
	[0x30]  = "OCID_PARAM",
	[0x31]  = "OCID_LEVEL",
	[0x32]  = "OCID_START",
	[0x33]  = "OCID_READ_DATA",
	[0x34]  = "ROUTE_DETECT",
	[0x35]  = "OCID_PARAM_LOSS",
	[0x100] = 0,  // 技巧，用于控制all数组的长度
    };
    if (cmd < 0x100 && cmd > 0) {
	printf("\t%s\r\n", all[cmd]);
    }
    return;
    // switch (cmd) {
    // case OTDR_RETURN:
    // 	printf("\tOTDR_RETURN\n");
    // 	break;
    // case OTDR_RESET:
    // 	printf("\tOTDR_RESET\n");
    // 	break;
    // case OTDR_PARAM:
    // 	printf("\tOTDR_PARAM\n");
    // 	break;
    // case OTDR_START:
    // 	printf("\tOTDR_START\n");
    // 	break;
    // case OTDR_STOP:
    // 	printf("\tOTDR_STOP/OCID_STOP\n");
    // 	break;
    // case OTDR_IS_FINISH:
    // 	printf("\tOTDR_IS_FINISH\n");
    // 	break;
    // case OTDR_REDLASER:
    // 	printf("\tOTDR_REDLASER\n");
    // 	break;
    // case FPGA_ALIVE:
    // 	printf("\tFPGA_ALIVE\n");
    // 	break;
    // case OTDR_COM_LASER:
    // 	printf("\tOTDR_COM_LASER\n");
    // 	break;
    // case FPGA_VER:
    // 	printf("\tFPGA_VER\n");
    // 	break;
    // case OTDR_ROTDR:
    // 	printf("\tOTDR_ROTDR\n");
    // 	break;
    // case OTDR_PULSE_ENCODE:
    // 	printf("\tOTDR_PULSE_ENCODE\n");
    // 	break;

    // case OCID_PARAM:
    // 	printf("\tOCID_PARAM\n");
    // 	break;
    // case OCID_LEVEL:
    // 	printf("\tOCID_LEVEL\n");
    // 	break;
    // case OCID_START:
    // 	printf("\tOCID_START\n");
    // 	break;
    // case OCID_READ_DATA:
    // 	printf("\tOCID_READ_DATA\n");
    // 	break;
    // case OTDR_RD_REG:
    // 	printf("\tOTDR_RD_REG\n");
    // 	break;
    // case OTDR_WD_REG:
    // 	printf("\tOTDR_WD_REG\n");
    // 	break;
    // case OTDR_ALARM_APPEAR:
    // 	printf("\tOTDR_ALARM_APPEAR\n");
    // 	break;
    // case OTDR_ALARM_DISAPPEAR:
    // 	printf("\tOTDR_ALARM_DISAPPEAR\n");
    // 	break;
    // case OCVL_PARAM:
    //     printf("\tOCVL_PARAM\n");
    //     break;
    // default :
    // 	printf("     ----------Unknow\n");
    // 	break;
    // }
}
#endif


/**
 * @brief	fpga 通过SPI方式传输
 * @param[in]	dev 设备描述符
 * @param[in]	cmd 发送命令
 * @param[in]	tbuf 发送内容
 * @param[in]	tlen 发送长度（不超过4000byte）
 * @param[in]	rbuf 读取内容
 * @param[in]	rlen 读取长度（不超过4000byte）
 * @param[in]   wait 发送tbuf内容后，等待多久读取rbuf，单位是us
 * @retval	null
 * @remark 调试版本中，在发送数据后固定延时5ms之后才读取数据，
    发布版可视情况删除
 */


unsigned int __fpga_transfer(struct fpga_dev *dev, unsigned char cmd, char *tbuf, int tlen, char *rbuf, int rlen, int wait)
{
    /*
    防止 struct fpga_2016 被编译器 n字节对齐，导致长度大于MIN_TX_PACK_SIZE
    末尾的 unsigned short 是校验码
    */
    memset(rbuf, 0xbb, rlen);
    assert(sizeof(struct fpga_2016) + sizeof(unsigned short) == MIN_TX_PACK_SIZE);
    WDG_CLEAR();

    char                    tx_head[sizeof(struct fpga_2016)];
    struct fpga_2016       *ptx     = (struct fpga_2016 *)tx_head;
    unsigned short          cksum   = 0;
    char                    unuse[] = { 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa };
    struct spi_ioc_transfer arr[]   = {
        // {
        // 	.tx_buf = (unsigned long)unuse,
        // 	.rx_buf = (unsigned long)NULL,
        // 	.len = ARRAY_SIZE(unuse),
        // 	.delay_usecs = 50000,
        // 	.cs_change = 0,
        // },
        {
		  .tx_buf      = (unsigned long)tx_head,
		  .rx_buf      = (unsigned long)NULL,
		  .len         = sizeof(struct fpga_2016),
		  .cs_change   = 0,
		  .delay_usecs = 100,
        },
        {
		  .tx_buf      = (unsigned long)tbuf,
		  .rx_buf      = (unsigned long)NULL,
		  .len         = tlen,
		  .cs_change   = 0,
		  .delay_usecs = wait,
        },
        {
		  .tx_buf      = (unsigned long)&cksum,
		  .rx_buf      = (unsigned long)NULL,
		  .len         = 2,
		  .cs_change   = 0,
		  .delay_usecs = wait,  // 发送完后给 FPGA 响应时间，再读取
        },
        {
		  .tx_buf    = (unsigned long)NULL,
		  .rx_buf    = (unsigned long)rbuf,
		  .len       = rlen,
		  .cs_change = 1,
        },
    };
    ptx->head[0] = 'G', ptx->head[1] = 'L', ptx->head[2] = 'I', ptx->head[3] = 'N', ptx->head[4] = 'K';
    ptx->unuse0 = 0;
    ptx->unuse1 = 0;
    ptx->addr   = 0;
    ptx->len    = 10 + tlen;
    ptx->cmd    = cmd;
    cksum       = cmd;
    cksum += _tx_checksum((uint8_t *)tbuf, tlen);

#if CONFIG_OTDR_PRINT_TX
    if (DBG_TX) {
	printf("*****************************");
	_show_cmd_name(cmd);
	printf("tx: %d byte", sizeof(struct fpga_2016));
	PrintfMemory((uint8_t *)tx_head, sizeof(struct fpga_2016));
	printf("buf: %d byte", tlen);
	PrintfMemory((uint8_t *)tbuf, tlen);
	printf("cksum: %d byte", 2);
	PrintfMemory((uint8_t *)&cksum, 2);
    }
#endif

    ioctl(dev->fd, SPI_IOC_MESSAGE(ARRAY_SIZE(arr)), &arr);
#if CONFIG_OTDR_PRINT_RX
    if (DBG_RX) {
	if (rlen > 64) {
	    rlen = 64;
	}
#if 0 && !defined(CONFIG_ISFINISH_RX)
		if (cmd == OTDR_IS_FINISH) {
			return -1;
		}
#endif
	PrintfMemory((uint8_t *)rbuf, rlen);
    }
#endif

    return -1;
}


/***************************************************************************
    OTDR 数据读取 功能入口 fpga_read_otdr_data

    fpga_read_otdr_data
        _read_otdr_first
        _read_otdr_serial
        _otdr_data_swap
        _print_otdr_data
        _otdr_checksum
            __checksum
***************************************************************************/

static unsigned int _read_otdr_serial(struct fpga_dev *dev, unsigned char *buf);
static void         _print_otdr_data(unsigned long *buf, int len);
static int          _otdr_data_swap(unsigned char *buf, int len);
#if defined(CONFIG__read_otdr_1)
static unsigned int _read_otdr_1(struct fpga_dev *dev, unsigned char *buf, int size);
#endif
#if defined(CONFIG__read_otdr_first)
static unsigned int _read_otdr_first(struct fpga_dev *dev, unsigned char *buf);
#endif

#if defined(CONFIG_USE_CKSUM)
static unsigned long __checksum(unsigned char *data, int len);
static unsigned long _otdr_checksum(unsigned char *data, unsigned long len);
#endif

#define ACK_OTDR_FRAME_LEN (18)

int fpga_read_otdr_data(
	struct fpga_dev *dev,
	unsigned char   *otdr_data,
	unsigned long    len)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);
    assert(dev->otdr_cache != NULL);
    assert(len == 16000 * 4 || len == 32000 * 4 || len == 64000 * 4 || len == 128000 * 4);

    char *rbuf = dev->otdr_cache;
    // return 0;
    /*
     * OTDR 数据格式，第一个0x23开头，后面每 4 byte为1数据点，每数据点 0x23开头
     * 0x23 | 0x23 xx xx xx | 0x23 xx xx xx | 0x23 xx xx xx
     *
     *         otdr_cache 最终存储的内容
     * |    0 byte             32000*4| 4000 |
     * |    OTDR 数据                 | 冗余 |
     * 后面的冗余内容是为了处理OTDR数据偏移而特意多读的
     */
    do_otdr_test_led(0);
    mutex_lock(&dev->mutex);
#if 1
    int find_23 = 0;
    printf("%s() %d\r\n", __FUNCTION__, __LINE__);
    this_dbg("%s() search '#' \r\n", __FUNCTION__);
    for (int j = 0; j < 3; j++) {
	/*
	 * 接收OTDR数据，只读取 1byte
	 */
	_fpga_transfer(dev, OTDR_ROTDR, &dev->ch, sizeof(dev->ch), dev->otdr_cache, 1);
	/*
	 * 逐个byte读取，在一定范围内寻找otdr数据帧头，
	 * 找到后就连快速读取后面的数据
	 */
#define CHECK_SERIAL (30)  // 在数据头N个字节里寻找标志
	for (int i = 0; i < CHECK_SERIAL; i++) {
	    //            printf("%s() %d\r\n", __FUNCTION__, __LINE__);
	    if (dev->otdr_cache[0] == '#') {
		find_23 = 1;
		goto _SerialRead;
	    }
	    _read_otdr_1(dev, (unsigned char *)(dev->otdr_cache), 1);
	}
    }


_SerialRead:;
    if (find_23 == 0) {
	ENV_PRINT_E("otdr data errr: can't find flag '#' (0-100 byte) %2.2x %2.2x %2.2x %2.2x\r\n",
	            *(dev->otdr_cache + 0),
	            *(dev->otdr_cache + 1),
	            *(dev->otdr_cache + 2),
	            *(dev->otdr_cache + 3));
	mutex_unlock(&dev->mutex);
	return -1;
    }

    /*
     * 快速读取后面数据，每次读取4000个点
     */
//    printf("%s() %d LEN_OTDR_CACHE %d 多4000个点\r\n", __FUNCTION__, __LINE__, LEN_OTDR_CACHE);
#if CONFIG_READ_1000_POINT
    for (int i = 0; i < LEN_OTDR_CACHE;) {
	printf("i = %d\r", i);
	_read_otdr_serial(dev, (unsigned char *)(dev->otdr_cache + i));
	i += 4000;  // 读取1000个点
    }
#else
    _read_otdr_serial(dev, (unsigned char *)(dev->otdr_cache));
#endif
#else
    // 一次将所有数据全部读出，目前只兼容16K、32K更高采样点需检测 len 后重新分配内存
    _fpga_transfer(dev, OTDR_ROTDR, NULL, 0, otdr_cache, LEN_OTDR_CACHE);
#endif

#if defined(CONFIG_OTDR_PRINT_RX)
    if (DBG_RX) {
	this_dbg("%s() peek receive data", __FUNCTION__);
	PrintfMemory((uint8_t *)rbuf, 16);
    }
#endif

    memcpy_char((char *)otdr_data,
                (char *)(rbuf + 0),
                len);
    mutex_unlock(&dev->mutex);


    /*
     * 数据都在 otdr_data 里，下面的数据doubleotdr_cache 的锁不再有效
     */
    __attribute__((unused)) int swap_err = _otdr_data_swap(otdr_data, len);
    _print_otdr_data((unsigned long *)otdr_data, len / 4);
#if defined(CONFIG_CHK_OTDR_FLAG) && !defined(CONFIG_USE_CKSUM)
    printf("%s(): %d\r\n", __FUNCTION__, __LINE__);
    if (swap_err == -1) {
	return -1;
    }
    return 0;
#else
    if (swap_err == -1) {
	return -1;
    }
    unsigned int sum = 0;
    if (env_unuse_cksum == 0) {
	sum = _otdr_checksum(otdr_data, len);
	if (sum == dev->checksum) {
	    this_dbg("%s() check sum OK\r\n", __FUNCTION__);
	    return 0;
	}
	else {
	    ENV_PRINT_W("check sum error cksum [%lx] otdr [%lx]\n", (uint32_t)dev->checksum, (uint32_t)sum);
	    return -1;
	}
    }
    else {
	return 0;
    }
#endif
}

/**
 * @brief	从SPI总线上读取若干字节
 * @param[in]	dev 设备描述符
 * @param[out]	buf 读取缓存
 * @param[in]	size 读取多少byte，如果该byte大于驱动缓存（默认4096），
 *		整个ioctl会报错，但函数不会有任何提示
 * @retval	0
 */
#if defined(CONFIG__read_otdr_1)
static unsigned int _read_otdr_1(struct fpga_dev *dev, unsigned char *buf, int size)
{
    struct spi_ioc_transfer arr[] = {
	{
		.tx_buf    = (unsigned long)NULL,
		.rx_buf    = (unsigned long)buf,
		.len       = size,
		.cs_change = 1,
	},
    };
    ioctl(dev->fd, SPI_IOC_MESSAGE(ARRAY_SIZE(arr)), &arr);
    PrintfMemory(buf, size);
    return 0;
}
#endif
/**
 * @brief	@ref fpga_otdr_isfinish 保证fpga数据准备好后，向FPGA发送读取指令，
            读取otdr数据第一帧，以1000个采样点为一帧，每点4byte.
            _read_otdr_first 不应该被外部应用直接调用，通过对外接口
            @ref fpga_read_otdr_data 间接调用
 * @param[in]		dev 设备描述符
 * @param[in][out]	buf 接受一串otdr数据，此数据缓存有效长度必须大于4000byte
 * @retval	0
 * @see	fpga_otdr_isfinish @see fpga_read_otdr_data
 * @see _read_otdr_serial
 */
#if defined(CONFIG_read_otdr_first)
static unsigned int _read_otdr_first(struct fpga_dev *dev, unsigned char *buf)
{
    char                    tx_head[sizeof(struct fpga_2016)];
    struct fpga_2016       *ptx   = (struct fpga_2016 *)tx_head;
    unsigned short          cksum = 0;
    struct spi_ioc_transfer arr[] = {
	{
		.tx_buf    = (unsigned long)tx_head,
		.rx_buf    = (unsigned long)NULL,
		.len       = sizeof(struct fpga_2016),
		.cs_change = 0,
	},
	{
		.tx_buf    = (unsigned long)NULL,
		.rx_buf    = (unsigned long)NULL,
		.len       = 0,
		.cs_change = 0,
	},
	{
		.tx_buf    = (unsigned long)&cksum,
		.rx_buf    = (unsigned long)NULL,
		.len       = 2,
		.cs_change = 0,
	},
	{
		.tx_buf    = (unsigned long)NULL,
		.rx_buf    = (unsigned long)buf,
		.len       = 4000,
		.cs_change = 1,
	},
    };
    ptx->head[0] = 'G', ptx->head[1] = 'L', ptx->head[2] = 'I', ptx->head[3] = 'N', ptx->head[4] = 'K';
    ptx->len = 10;
    ptx->cmd = OTDR_ROTDR;
    cksum    = OTDR_ROTDR;
    cksum += _tx_checksum((uint8_t *)buf, 0);


    ioctl(dev->fd, SPI_IOC_MESSAGE(ARRAY_SIZE(arr)), &arr);

    return 0;
}
#endif

/**
 * @brief	@ref _read_otdr_first 读取第一帧之后，连续读取otdr数据剩下各帧，
        各帧之间没有任何命令码，以1000个采样点为一帧，每点4byte
        _read_otdr_serial 不应该被外部应用直接调用，通过对外接口
        @ref fpga_read_otdr_data 间接调用
 * @param[in]		dev 设备描述符
 * @param[in][out]	buf 接受一串otdr数据，此数据缓存有效长度必须大于4000byte
 * @retval	0
 * @see	_read_otdr_first fpga_read_otdr_data
 */

static unsigned int _read_otdr_serial(struct fpga_dev *dev, unsigned char *buf)
{
    struct spi_ioc_transfer arr[] = {
	{
		.delay_usecs = 1000,
		.tx_buf      = (unsigned long)NULL,
		.rx_buf      = (unsigned long)buf,
#if CONFIG_READ_1000_POINT
		.len = 4000,
#else  // 根据32k或64k修改
		.len = 4000 * 32,
#endif
		.cs_change = 0,
	},
    };
    ioctl(dev->fd, SPI_IOC_MESSAGE(ARRAY_SIZE(arr)), &arr);
    return 0;
}

#define SWAP 1
/**
 * @brief	交换otdr数据
 * @param[in][out]	buf otdr数据流
 * @param[in]		len otdr数据流长度，1 byte为单位
 * @bug	数据内容采用大端格式，由来原因:\n
    fpga在传输数据时已4byte为单位，通常第一个byte最容易错误，如传输如下数据	\n
    0x00030201 0x00060504 						\n
    发送方										\n
    0x00 0x03 0x02 0x01 0x00 0x06 0x05 0x04		\n
    接收方 0xnn可能是乱码						\n
    0xnn 0x03 0x02 0x01 0xnn 0x06 0x05 0x04		\n
    接收方将最高位8bit清0，						\n
    0x00 0x03 0x02 0x01 0x00 0x06 0x05 0x04		\n
    接收方小端格式								\n
    0x01 0x02 0x03 0x00 0x04 0x05 0x06 0x00		\n
    接收以4byte为单位显示						\n
    0x00030201 0x00060504 						\n

 * @see fpga_read_otdr_data
 */
static int _otdr_data_swap(unsigned char *buf, int len)
{
#if defined(CONFIG_OTDR_SWAP)
    unsigned char *pchar = (unsigned char *)buf;
    char           tmp[4];

    for (int i = 0; i < len;) {
#if defined(CONFIG_CHK_OTDR_FLAG)
	if (pchar[0] != '#') {
	    ENV_PRINT_E("error otdr data index %i\n", i);
	    return -1;
	}
#endif

#if defined(CONFIG_23_23aaBBcc_23aaBBcc)
	tmp[0] = pchar[0];
	tmp[1] = pchar[1];
	tmp[2] = pchar[2];
	tmp[3] = pchar[3];

	pchar[3] = 0;
	pchar[2] = tmp[1];
	pchar[1] = tmp[2];
	pchar[0] = tmp[3];
#endif  // end CONFIG_23_23aaBBcc_23aaBBcc
        // TODO 其他数据格式
	pchar += 4;
	i += 4;
    }
    return 0;
#else   // CONFIG_OTDR_SWAP
    return 0;
#endif  // end CONFIG_OTDR_SWAP
}

/**
 * @brief	格式化方式"简略"打印otdr数据(以4byte一个点为单位)
 * @param	buf otdr点数据
 * @param	len 点数
 * @remarks 需要编译时使能CONFIG_PRINT_OTDR_DATA，在运行时使能DBG_BIT_OTDR_DATA\n
    打印5000个点格式化:\n
 @code
    ..............0 - 10 (step 1)
    pt0001 pt0002 pt0003 pt0004 pt0005 pt0006 pt0007 pt0008 pt0009 pt0010
    ..............0 ~ 5000  (step 100)
    pt0100 pt0200 pt0300 pt0400 pt0500 pt0600 pt0700 pt0800 pt0900 pt1000
    pt1100 pt1200 pt1300 pt1400 pt1500 pt1600 pt1700 pt1800 pt1900 pt2000
    pt2100 pt2200 pt2300 pt2400 pt2500 pt2600 pt2700 pt2800 pt2900 pt3000
    pt3100 pt3200 pt3300 pt3400 pt3500 pt3600 pt3700 pt3800 pt3900 pt4000
    pt4100 pt4200 pt4300 pt4400 pt4500 pt4600 pt4700 pt4800 pt4900 pt5000
    ..............4991  ~  5000 (step 1)
    pt4991 pt4992 pt4993 pt4994 pt4995 pt4996 pt4997 pt4998 pt4999 pt5000
 @endcode
 */
static void _print_otdr_data(unsigned long *buf, int len)
{
#ifdef CONFIG_PRINT_OTDR_DATA
    if (0 == DBG_BIT_OTDR_DATA) {
	return;
    }
    unsigned long *plong;
    unsigned char *pchar;


    printf(C_DEBUG "\n..............0 - 10 (step 1)\n");
    plong = (unsigned long *)buf;

    for (int i = 0; i < 10; i++) {
	printf("%8.8x ", (unsigned int)*plong++);
    }

    printf("\n\n..............0 ~ %d  (step 100)", len);
    plong = (unsigned long *)buf;
    pchar = (unsigned char *)plong;
    // for (int i = 0; i < len / sizeof(unsigned long); i += 100) {
    for (int i = 0; i < len; i += 100) {
	if (i % 1000) {
	    printf(" ");
	}
	else {
	    printf("\n");
	}
	printf("%8.8x", (unsigned int)*(plong + i));
    }

    printf("\n\n..............%d  ~  %d (step 1)\n", len - 10, len);
    plong = (unsigned long *)(buf + len - 10);
    // plong = (unsigned long *)(buf + len- 10);
    for (int i = 0; i < 10; i++) {
	printf("%8.8x ", (unsigned int)*plong);
	plong++;
    }

    printf(C_NORMAL "\n");
#else
#endif
}

#if defined(CONFIG_DUMP_OTDR_DATA)
static void _save_file(char *fname, unsigned int *pt, unsigned int len)
{
    FILE *fp;
    fp = fopen(fname, "wb");

    for (int i = 0; i < len; i++) {
	// fprintf(fp, "%8.8x\r\n", *(pt + i) & 0x00ffffff);
	fprintf(fp, "%d\r\n", *(pt + i) & 0x00ffffff);
	// fprintf(fp, "%8.8x\r\n", *(pt + i));
    }
    fclose(fp);
}
#endif

/**
 * @brief	计算连续数据校验和，数据必须是4byte 倍数，数据已4byte为单位相加。
    如:\n
    0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08
    chksum = 0x04030201 + 0x08070605
 * @param	data 待计算数据
 * @param	len 数据长度，必须是4byte倍数
 * @retval 校验值
 * @see	_otdr_checksum
 */
#if defined(CONFIG_USE_CKSUM)
static unsigned long __checksum(unsigned char *data, int len)
{
    unsigned int *plong;
    // unsigned long *plong;
    unsigned long      loop = len >> 2;
    unsigned long long sum  = 0, tmp;


#if defined(CONFIG_DUMP_CKSUM)
    FILE *fp;
    fp = fopen("cksum.txt", "wb");
#endif
    plong = (unsigned long *)data;

    for (int i = 0; i < loop; i++) {
	tmp = (unsigned long)data[0] +
	      (unsigned long)(data[1] << 8) +
	      (unsigned long)(data[2] << 16) +
	      (unsigned long)(data[3] << 24);
#if defined(CONFIG_DUMP_CKSUM)
	fprintf(fp, "%8.8x... \r\n", *plong);
#endif
	sum += *plong++;
    }
#if defined(CONFIG_DUMP_CKSUM)
    fclose(fp);
#endif
    return sum;
}
#endif
/**
 * @brief	计算OTDR数据校验和。若干次调用 @ref __checksum 计算出校验值，
        计算方法与实际设备特性有关，没有统一算法
 * @param	data 待计算数据
 * @param	len 数据长度，必须是4byte倍数
 * @retval 校验值
 * @see	__checksum
 */
#if defined(CONFIG_USE_CKSUM)
static unsigned long _otdr_checksum(unsigned char *data, unsigned long len)
{
    unsigned long sum = 0;

    /*
      otdr数据已4byte为一个点
      1. 去掉第一个点计算其余点校验和
      2. 原校验和 + 第一个点 "3次" 得出最终校验和
    */
    // sum =  __checksum(data + 4, len - 4);
    // sum += __checksum(data, 4);
    // sum += __checksum(data, 4);
    // sum += __checksum(data, 4);
#if defined(CONFIG_DUMP_OTDR_DATA)
    //_save_file("otdr.txt", data, len / 4);
#endif
    sum = __checksum(data, len);
    return sum;
}
#endif
/***************************************************************************
    End OTDR 数据读取 功能入口 fpga_read_otdr_data
***************************************************************************/
struct sw
{
    unsigned int  ctrl;
    unsigned long bit;
};
unsigned long fpga_dbg_sw(struct fpga_dev *dev, struct fpga_test_param *param, int sw_index)
{
    struct sw list[] = {
	CFG_SW0,
	CFG_SW1,
	CFG_SW2,
	CFG_SW3,
	CFG_SW4,
	CFG_SW5,
	CFG_SW6,
	CFG_SW7,
	CFG_SW8,
	CFG_SW9,
	CFG_SW10,
	CFG_SW11,
	CFG_SW12,
	CFG_SW13,
	CFG_SW14,
	CFG_SW15,
	{ SW_UNUSE, UNUSE_BIT },
    };
    assert(sw_index < ARRAY_SIZE(list));
    if (sw_index >= ARRAY_SIZE(list)) {
	ENV_PRINT_W("un support switch %d", sw_index);
	return -1;
    }
    param->en_hw_amp     = 1;
    param->hw_amp        = 0;
    param->en_hw_apd_vol = 1;
    param->hw_apd_vol    = 0;
    switch (list[sw_index].ctrl) {
    case SW_UNUSE:
	printf("switch unuse\n");

	break;
    case SW_RESERVE:
	printf("switch reserve\n");
	break;
    case SW_AMP:
	param->hw_amp = list[sw_index].bit;
	break;
    case SW_APD:
	param->hw_apd_vol = list[sw_index].bit;
	break;
    case SW_POWER:
	printf("switch power\n");
	break;
    }
    fpga_otdr_param(dev, param);
    fpga_otdr_start(dev);
    return 0;
}


int fpga_alarm(struct fpga_dev *dev, int ch, int is_appear)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    char tbuf[2];
    char rbuf[MIN_RX_PACK_SIZE];
    int  cmd = OTDR_ALARM_APPEAR;

    if (is_appear != 1) {
	cmd = OTDR_ALARM_DISAPPEAR;
    }

    tbuf[0] = ch;
    tbuf[1] = 0;  // 无用

    mutex_lock(&dev->mutex);
    _fpga_transfer(dev, cmd, tbuf, ARRAY_SIZE(tbuf), rbuf, ARRAY_SIZE(rbuf));
    mutex_unlock(&dev->mutex);

    unsigned char ack = ACK_CODE(rbuf);

    switch (ack) {
    case 0xaa:
	this_dbg("%s() exist\r\n", __FUNCTION__);
	return 0;
    case 0x55:
	this_dbg("%s() free\r\n", __FUNCTION__);
	return -1;
    default:
	ENV_PRINT_E("unknow\r\n");
	return -1;
    }
}
//*****************************************************************************
// End OTDR 系列接口
//*****************************************************************************


//*****************************************************************************
// TKxx系列接口
//*****************************************************************************


int fpga_ocid_param(struct fpga_dev *dev, struct fpga_ocid_test_param *val)
{
    // ENV_PRINT_W("未实现");
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    struct _fpga_ocid_param_tx tbuf;

    tbuf.ch   = val->ch & 0xff;
    tbuf.post = val->post;


    // ENV_PRINT_W("Encode:\r\n");
    // ENV_PRINT_W("    ch             %4.4x\n", tbuf.ch);
    // ENV_PRINT_W("    post             %4.4x\n", tbuf.post);

    dev->ch = tbuf.ch;

    char          rbuf[MIN_RX_PACK_SIZE];
    unsigned char ack;

    for (int retry = 0; retry < RETRY; retry++) {
	_fpga_transfer(dev, OCID_PARAM, (char *)&tbuf, sizeof(struct _fpga_ocid_param_tx), rbuf, ARRAY_SIZE(rbuf));
	ack = ACK_CODE(rbuf);
	switch (ack) {
	case 0xaa:
	    this_dbg("%s() OK\r\n", __FUNCTION__);
	    // usleep(10000);// FPGA控制光耦
	    goto _End;
	case 0x55:
	    ENV_PRINT_W("Error\r\n");
	    return -1;
	default:
	    ENV_PRINT_E("unknow\r\n");
	    return -1;
	}
    }
    return -1;
_End:
    return fpga_ocid_sensitive(dev, val->sensitive);
}
int fpga_ocid_sensitive(struct fpga_dev *dev, int sensitive)
{
    // ENV_PRINT_W("未实现，可能以后作废");
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    char tbuf[3];
    char rbuf[MIN_RX_PACK_SIZE];


    tbuf[0] = dev->ch;
    tbuf[1] = sensitive & 0xff;
    tbuf[2] = (sensitive >> 8) & 0xff;
    // ENV_PRINT_W("发送3个字节，FPGA是否正确接收???\r\n");
    // ENV_PRINT_W("先发通道 %x 后发电平 [%x %x]\r\n", tbuf[0], tbuf[1], tbuf[2]);
    mutex_unlock(&dev->mutex);
    _fpga_transfer(dev, OCID_LEVEL, tbuf, ARRAY_SIZE(tbuf), rbuf, ARRAY_SIZE(rbuf));
    mutex_unlock(&dev->mutex);

    unsigned char ack = ACK_CODE(rbuf);

    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	return 0;
    case 0x55:
	ENV_PRINT_W("Error\r\n");
	return -1;
    default:
	ENV_PRINT_E("unknow\r\n");
	return -1;
    }
    return -1;
}


/*
  里面的数据是小端顺序
  以字节方式打印  aa bb cc 23
  小端4字节对齐后 23 cc bb aa
  去掉标志        00 cc bb aa
*/
void _ocid_clear_flag(unsigned int *data, unsigned long len)
{
    unsigned long *p32 = (unsigned long *)data;
    for (int i = 0; i < len; i++) {
	*p32 = *p32 & 0x00ffffff;
	p32++;
    }
}
int fpga_read_ocid_data(
	struct fpga_dev *dev,
	unsigned int    *ocid_data,
	unsigned long    len,
	unsigned long   *rlen)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    char rbuf[MIN_RX_PACK_SIZE];
    // 验证传入的内存不是非法
    bzero(ocid_data, sizeof(unsigned int) * len);
    this_dbg("%s() \r\n", __FUNCTION__);

    mutex_unlock(&dev->mutex);
    _fpga_transfer(dev, OCID_READ_DATA, &dev->ch, sizeof(dev->ch), ocid_data, sizeof(unsigned int) * len);
    mutex_unlock(&dev->mutex);

    _ocid_clear_flag(ocid_data, len);
    *rlen = len;
    return 0;
}

int fpga_ocid_start(struct fpga_dev *dev)
{
    // ENV_PRINT_W("未实现");

    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    char rbuf[MIN_RX_PACK_SIZE];

    mutex_unlock(&dev->mutex);
    _fpga_transfer(dev, OCID_START, &dev->ch, sizeof(dev->ch), rbuf, ARRAY_SIZE(rbuf));
    mutex_unlock(&dev->mutex);

    unsigned char ack = ACK_CODE(rbuf);

    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	return 0;
    case 0x55:
	ENV_PRINT_W("Error\r\n");
	return -1;
    default:
	ENV_PRINT_E("unknow\r\n");
	return -1;
    }
    return -1;
}
int fpga_ocid_stop(struct fpga_dev *dev)
{
    // ENV_PRINT_W("未实现");
    return fpga_otdr_stop(dev);
}

//*****************************************************************************
// End TKxx系列接口
//*****************************************************************************

//*****************************************************************************
// TF500系列接口
//*****************************************************************************

// tf500
int fpga_wd_reg(struct fpga_dev *dev, char *data)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);

    // 写入内容两个字节，第二个字节填0
    char tbuf[2];
    char rbuf[MIN_RX_PACK_SIZE];

    tbuf[0] = *data;
    tbuf[1] = 0;
    mutex_lock(&dev->mutex);
    _fpga_transfer(dev, OTDR_WD_REG, tbuf, ARRAY_SIZE(tbuf), rbuf, ARRAY_SIZE(rbuf));
    mutex_unlock(&dev->mutex);


    unsigned char ack = ACK_CODE(rbuf);
    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	return 0;
    case 0x55:
	ENV_PRINT_W("Error\r\n");
	return -1;
    default:
	ENV_PRINT_E("unknow\r\n");
	return -1;
    }
}


int fpga_rd_reg(struct fpga_dev *dev, char *data)
{
    assert(dev != NULL);
    assert(dev->desc == dev_desc);
    assert(data != NULL);
    *data = 0;

    char rbuf[MIN_RX_PACK_SIZE];

    mutex_lock(&dev->mutex);
    __fpga_transfer(dev, OTDR_RD_REG, NULL, 0, rbuf, ARRAY_SIZE(rbuf),
                    _1MS);  // 等待1ms给FPGA从外置存储器读取数据时间
    mutex_unlock(&dev->mutex);


    unsigned char ack = ACK_CODE(rbuf);
    switch (ack) {
    case 0xaa:
	this_dbg("%s() OK\r\n", __FUNCTION__);
	*data = rbuf[1];
	return 0;
    case 0x55:
	ENV_PRINT_W("Error\r\n");
	return -1;
    default:
	ENV_PRINT_E("unknow\r\n");
	return -1;
    }
}

//*****************************************************************************
// End TF500系列接口
//*****************************************************************************

#ifdef PRJ_NAME
MODULE_PROJECT(PRJ_NAME);
#endif
#if defined(PRJ_VERSION) && defined(PRJ_PATCHLEVEL) && defined(PRJ_SUBLEVEL)
MODULE_VERSION(PRJ_VERSION "." PRJ_PATCHLEVEL "." PRJ_SUBLEVEL);
#endif

MODULE_BUILD(__DATE__ " " __TIME__);

char str_config[] = "fffa";
MODULE_AUTHOR("MenglongWu <MenglongWoo@aliyun.com>");
MODULE_DEPEND("spi_mxs.ko spidev.ko libpthread.so");
MODULE_DEPEND("Board: TR700");
MODULE_DESCRIPTION("The GLink OTDR FPGA communicate interface");
#ifdef CONFIG_CHK_OTDR_FLAG
PROG_INFO(config, "CONFIG_CHK_OTDR_FLAG=Y");
#else
PROG_INFO(config, "CONFIG_CHK_OTDR_FLAG=N");
#endif
#ifdef CONFIG_USE_CKSUM
PROG_INFO(config, "CONFIG_USE_CKSUM=Y");
#else
PROG_INFO(config, "CONFIG_USE_CKSUM=N");
#endif

#ifdef CONFIG_TEMP_FROM_FPGA
PROG_INFO(config, "CONFIG_TEMP_FROM_FPGA=Y");
#endif
#ifdef CONFIG_TEMP_FROM_ARM
PROG_INFO(config, "CONFIG_TEMP_FROM_ARM=Y");
#endif

#ifdef CONFIG_MAX_SPI_TR_4000BYTE
PROG_INFO(config, "CONFIG_MAX_SPI_TR_4000BYTE=Y");
#endif
#ifdef CONFIG_MAX_SPI_TR_32000BYTE
PROG_INFO(config, "CONFIG_MAX_SPI_TR_32000BYTE=Y");
#endif

PROG_GITSHA1(__GIT_SHA1__);
#warning("------- 这是一个即将废弃的函数------ddd")
